import { debounce } from '@/utils/common'
import { action, makeObservable, observable } from 'mobx'

/**
 * 获取缓存值
 * @param defaultValue 获取失败的返回默认值
 * @param key 密钥
 * @returns
 */
const getLocal = <T>(
  defaultValue: T,
  key: string,
  storage = localStorage
): T => {
  let data: T
  try {
    const str = storage.getItem(key)
    if (!str) {
      return defaultValue
    }
    data = JSON.parse(str)
    if (data == null) {
      return defaultValue
    }
  } catch (_: any) {
    data = defaultValue
  }
  return data
}
class StorageState<T extends object> {
  value: T
  private key: string
  private defaultValue: T
  private saveLocal: (data: T) => void
  private storage: Storage
  setValue<K extends keyof T>(key: K, value: T[K]) {
    this.value[key] = value
    this.saveLocal(this.value)
  }
  set(data: Partial<T>) {
    for (const key in data) {
      this.setValue(key as keyof T, data[key])
    }
  }
  clear() {
    this.value = this.defaultValue
    this.saveLocal(this.value)
  }
  /**
   *
   * @param key 缓存密钥
   * @param defaultValue 默认值
   * @param persistenceDelay 持久化延迟
   */
  constructor(
    key: string,
    defaultValue: T,
    persistenceDelay: number = 1000,
    storage = localStorage
  ) {
    this.storage = storage
    if (defaultValue === null) {
      throw new Error('new StorageState param defaultValue cannot be null')
    }
    this.key = '_storage_state_' + key
    if (key.length < 5) {
      console.log('key too short, please use a longer key')
    }
    this.value = getLocal<T>(defaultValue, this.key, this.storage)
    this.defaultValue = defaultValue
    for (const key in defaultValue) {
      if (this.value[key] === undefined) {
        this.value[key] = defaultValue[key]
      }
    }
    if (persistenceDelay <= 0) {
      this.saveLocal = <T>(data: T) =>
        this.storage.setItem(this.key, JSON.stringify(data))
    } else {
      this.saveLocal = debounce(
        <T>(data: T) => this.storage.setItem(this.key, JSON.stringify(data)),
        persistenceDelay
      )
    }

    makeObservable(this, {
      value: observable,
      setValue: action,
    })
  }

  /**
   * 用来扩展一下业务代码
   * @param obj
   * @returns
   */
  mixins<M extends ExtendStorageState<T>>(
    obj: M
  ): Override<StorageState<T>, M> {
    // Object.assign(this, obj)
    for (const key in obj) {
      // @ts-ignore
      this[key] = obj[key]
    }
    // @ts-ignore
    return this
  }
}
type ExtendStorageState<T extends object> = {
  [key in keyof StorageState<T>]?: `冲突: Mixin 不能包含 '${key &
    string}' 属性，因为它已经存在于基类中`
} & {
  [key: string]: any
}
// keyof InstanceType<typeof StorageState>
type IntersectKeys<T, U> = Extract<keyof T, keyof U>
type Override<T, U> = Omit<U, IntersectKeys<T, U>> & T
export default StorageState
